home *** CD-ROM | disk | FTP | other *** search
- /*
- * Interrupt.c - This file demonstrates ways to deal with hardware interrupts.
- *
- * Mark D. Rustad. 6/11/90.
- *
- * Note that this won't compile and certainly won't run. Just make note of
- * the techniques demonstrated.
- */
-
- #include <clister.h>
- #include <os.h>
- #include <arose.h>
-
- _TITLE("Interrupt.c - This file demonstrates ways to deal with hardware interrupts.")
- _SPACE("4,20,Definitions")
- /*
- * Definitions.
- */
-
- /* The following saves A5 in the indicated location on the card. */
-
- pascal void SaveA5_780(void) = {0x21CD, 0x0780}; // Save A5 in 0x0780
-
- /*
- * Why I can get away with the above: A/ROSE puts it's stuff starting at 0x400.
- * A/ROSE exception handling dumps the processor state into 0x600 and A/ROSE
- * code normally begins at 0x800. This means that all the interrupt vector
- * space below 0x400 is available for use as well as space between the A/ROSE
- * exception area and 0x800. It is reasonable for a designer designing code
- * that will control hardware on a card to make use of these areas (certainly
- * the interrupt vectors should be available.
- *
- * It would be a very bad idea for a piece of fairly general purpose,
- * dynamically downloadable A/ROSE code to make any use of any of these areas,
- * since they may be in use by whatever is controlling the hardware on the
- * card it may be downloaded onto.
- *
- * Since this example is dealing with interrupts, it must be controlling some
- * hardware.
- */
-
- pascal void Illegal() = 0x4AFC; // Now we die!
-
- typedef void (*funcPtr)();
-
- _SPACE("4,20,Globals")
- /*
- * Globals.
- */
-
- AROSEmessage *IntMsg; // Points to message to be sent from interrupt rtn.
-
- _SPACE("4,20,main")
- /*
- * main - No main, no gain...
- */
-
- void main(void)
- {
- register AROSEmessage *mp;
- void IntRtn(AROSEmessage *);
- extern void Interrupt(void);
-
- /*
- * Initialize message for interrupt usage.
- */
-
- if ((mp = GetMsg()) == 0)
- {
- /* Most people just give up when there's a shortage
- * of message buffers at initialization. Why should
- * this example be any different?
- */
- Illegal();
- }
-
- mp->mTo = mp->mFrom; // Since mFrom has our TID already
- mp->mPriority = 100; // Process interrupt messages first!
- mp->mCode = 0x1001; // My mCode for interrupt messages
- mp->mSData[0] = (long) IntRtn; // Pointer to routine
- IntMsg = mp; // Place in global
-
- SaveA5_780(); // Save our A5 value in 0x780 for interrupt routine
-
- /*
- * Now make hardware interrupt vector point at assembly routine.
- */
-
- *(long*)0x6C = (long) Interrupt;
-
- /*
- * Lots of other stuff to do, for "real" things...
- */
-
- DoOtherStuff();
-
- /*
- * Main loop.
- */
-
- for (;;)
- {
- mp = Receive(0, 0, 0, 0);
-
- /* This checks for completions from the next server. This approach
- * assumes that the next server does not make requests to us, but
- * only replies. Checks could be made on mCode.
- */
-
- if (mp->mCode == 0x1001)
- {
- ((funcPtr)mp->mSData[0])(mp); // Call routine to complete request
- continue; // Back up to the top of the for loop
- }
-
- /*
- * This is probably followed by a normal mCode decode for requests.
- * Omitted here for brevity. See MiddleMan.c for one example.
- */
- }
- }
-
- _SPACE("4,20,IntRtn")
- /*
- * IntRtn - This routine gets called as a result of the interrupt routine
- * sending a message.
- *
- * Inputs:
- * mp - Points to the actual message buffer.
- */
-
- void IntRtn(AROSEmessage *mp)
- {
- /*
- * Process the data that the interrupt routine was trying to tell us
- * about. This probably involves accessing some shared structures, etc.
- * specific to your application. Often the shared structures may take
- * the form of some sort of circular buffer or list.
- */
-
- IntMsg = mp; // Enable subsequent notification
-
- if (WorkToDo)
- DoTheWork();
- }
-
- /*
- * Now let's play make believe (yes, my 3 year old is rubbing off on me).
- * Let's pretend that MPW C can switch to assembly just to keep everything for
- * this example in one file. This is pure fantasy, folks...
- */
-
- #pragma asm on
-
- Interrupt Proc Export
- Import IntMsg:Data
- Import PostRTE:Code // A/ROSE exit
-
- MoveM.L A0/A1/A5/D0-D2, -(A7) // Save registers (assume we may call C code)
- MoveA.L $780, A5 // Set A5
-
- SROffset Equ 5*4 // Size of registers on stack
-
- * Probably mess with the hardware here.
-
- ...
- ...
-
- * Notify mainline of work to do.
-
- MoveA.L IntMsg, A0
- MoveQ #0, D0
- Move.L D0, IntMsg // Indicate message in use
-
- * We can get really fancy if we need to, by enabling interrupts while
- * the Send takes place. This isn't always needed, but has been known to
- * be useful in high-speed non-DMA synchronous environments. It is shown here
- * as an interesting example.
-
- Move.L A0, D0 // See if we got the buffer
- BEq.S @leave // If no, get out
-
- Move.W SROffset(A7), D0 // Get previous SR off stack
- OrI.W #$2000, D0 // Be sure supervisor bit remains set!
- Move D0, SR // Lower to previous IPL
-
- * Note that lowering the IPL will not cause a pile-up of messages to arrive
- * at the main task because only one message is available. This approach assumes
- * that there are other data structures that the mainline looks at to locate
- * work to do. Notifications of some events may not work well this way. In
- * those cases, it is reasonable to keep a short list of pre-allocated message
- * buffers and use those - perhaps even falling back on calling GetMsg if the
- * list should ever be exhausted (GetMsg can be called from an interrupt routine,
- * but it takes more time than one would like).
-
- * Continue with message send.
-
- Move.L D1, mMessage.mOData(A0) // Maybe put some info in the message
- Send A0 // Send the message
- ; Trap #trSend // This instead of "Send" will save 4 cycles
-
- * Hopefully the Send macro will get fixed to only generate the Trap instruction
- * when the argument is simply A0.
-
- @leave MoveM.L (A7)+, A0/A1/A5/D0-D2 // Restore registers
- Jmp PostRTE // Go through standard A/ROSE exit code
-
- EndP
-